#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDateTime>
#include <QTimer>
#include <QSound>
#include <QProcess>
#include <QDir>
#include <QSettings>
#include <time.h>

#include "favoritesfiltermodel.h"
#include <hyperspinmodel.h>
#include "rightitemdelegate.h"
#include "leftitemdelegate.h"
#include "exitdialog.h"

#include <Windows.h>

bool operator>(const QSize& a, const QSize& b)
{
  return a.width() > b.width() || a.height() > b.height();
}

MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  _ui(new Ui::MainWindow),
  _sound(NULL),
  _artTimer(new QTimer(this)),
  _exiting(false),
  _keySleepTimer(new QTimer()),
  _inputThread(new InputThread(this)),
  _eventBlocker(new NativeEventBlocker(this))
{
  srand(time(NULL));

  _ui->setupUi(this);
  _ui->items->setItemDelegate(new LeftItemDelegate);
  _ui->systemActions->setItemDelegate(new RightItemDelegate);
  _ui->gameActions->setItemDelegate(new RightItemDelegate);

  updateDate();
  QTimer* dateTimer = new QTimer(this);
  connect(dateTimer,SIGNAL(timeout()),this,SLOT(updateDate()));
  dateTimer->setInterval(10000);
  dateTimer->start();

  _keySleepTimer->setInterval(60000);
  connect(_keySleepTimer,SIGNAL(timeout()),this,SLOT(keySleep()));
  _keySleepTimer->start();

  switchToSystem(hs::MAIN_MENU);

  _artTimer->setSingleShot(true);
  _artTimer->setInterval(500);
  connect(_artTimer,SIGNAL(timeout()),this,SLOT(loadArt()));
  connect(_ui->videoPlayer,SIGNAL(finished()),this,SLOT(loadArt()));
}

MainWindow::~MainWindow()
{
  delete _ui;
  _inputThread->terminate();
}

void MainWindow::enableControls()
{
  foreach(QWidget* w, findChildren<QWidget*>())
    w->installEventFilter(_eventBlocker);

  connect(_inputThread,SIGNAL(key(int)),this,SLOT(key(int)));
  _inputThread->init(this);
  _inputThread->start();
}

void MainWindow::updateDate()
{
  QDateTime dt = QDateTime::currentDateTime();
  _ui->hourLabel->setText(dt.toString("hh:mm AP"));
  _ui->dateLabel->setText(dt.toString("ddd, dd MMM yyyy"));
}

void MainWindow::keySleep()
{
  if (_currentView != _ui->items)
    return;
  int k = rand()%2 == 0 ? Ak_Up : Ak_Down;
  int count = rand()%600 + 100;

  for (int i=0;i<count;++i)
  {
    key(k,false);
    QApplication::processEvents(QEventLoop::AllEvents);
  }
}

void MainWindow::key(int k, bool sound)
{
  _keySleepTimer->stop();

  if (_exiting)
    return;

  switch ((ApplicationKey)k)
  {
  case Ak_Up:
    if (sound) playSound(hs::WAV_UP);
    moveRow(_currentView,-1);
    break;
  case Ak_Down:
    if (sound) playSound(hs::WAV_DOWN);
    moveRow(_currentView,1);
    break;
  case Ak_Left:
    if (sound) playSound(hs::WAV_LEFT);
    pageRow(_currentView,-1);
    break;
  case Ak_Right:
    if (sound) pageRow(_currentView,+1);
    playSound(hs::WAV_RIGHT);
    break;
  case Ak_Enter:
    if (sound) playSound(hs::WAV_ENTER);
    enter();
    break;
  case Ak_Back:
    if (sound) playSound(hs::WAV_BACK);
    back();
    break;
  }

  _keySleepTimer->start();
}

void MainWindow::leftIndexChanged(const QModelIndex& current)
{
  _artTimer->stop();
  _artTimer->start();

  if (_system != hs::MAIN_MENU)
  {
    _ui->gameActions->item(1)->setHidden(current.data(hs::FavoriteRole).toBool());
    _ui->gameActions->item(2)->setHidden(!(current.data(hs::FavoriteRole).toBool()));
  }

  _ui->actionsTitle->setText(current.data().toString());
}

void MainWindow::loadArt()
{
  _ui->videoPlayer->play(_ui->items->currentIndex().data(hs::ArtRole).toString());
}

void MainWindow::switchToSystem(const QString& s, bool fav)
{
  _system = s;

  HyperspinModel* sourceModel = new HyperspinModel(s);
  if (fav)
  {
    FavoritesFilterModel* filterModel = new FavoritesFilterModel();
    filterModel->setSourceModel(sourceModel);
    _ui->items->setModel(filterModel);
  }
  else
  {
    _ui->items->setModel(sourceModel);
  }

  _currentView = _ui->items;
  connect(_ui->items->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(leftIndexChanged(QModelIndex)));
  _ui->items->setCurrentIndex(_ui->items->model()->index(0,0));
  moveRow(_ui->items,0);
  clearSelection(_ui->systemActions);
  clearSelection(_ui->gameActions);
  _ui->systemActions->setVisible(s == hs::MAIN_MENU);
  _ui->gameActions->setVisible(s != hs::MAIN_MENU);

  _ui->systemsTitle->setText(s == hs::MAIN_MENU ? "Systems" : "Games");
  _ui->centralWidget->setBackground(s == hs::MAIN_MENU ? hs::BG_SYSTEMS : hs::BG_GAMES);

}

void MainWindow::enter()
{
  if (_currentView == _ui->items)
  {
    _currentView = _ui->systemActions->isVisible() ? _ui->systemActions : _ui->gameActions;
    _currentView->setCurrentIndex(_currentView->model()->index(0,0));
  }
  else
  {
    if (_system == hs::MAIN_MENU) // View games
    {
      switchToSystem(_ui->items->currentIndex().data(hs::NameRole).toString(), _ui->systemActions->currentIndex().row() == 1);
    }

    else
    {
      if (_ui->gameActions->currentRow()>=1) // Add/remove from favorites
      {
        HyperspinModel* sourceModel = dynamic_cast<HyperspinModel*>(_ui->items->model());
        if (sourceModel == NULL)
        {
          sourceModel = static_cast<HyperspinModel*>(static_cast<FavoritesFilterModel*>(_ui->items->model())->sourceModel());
        }
        sourceModel->setFavorite(_ui->items->currentIndex().data(hs::NameRole).toString(),!_ui->items->currentIndex().data(hs::FavoriteRole).toBool());
        leftIndexChanged(_ui->items->currentIndex());
        _ui->gameActions->setCurrentRow(_ui->gameActions->currentRow()==1 ? 2 : 1);
      }
      else // run
      {
        run();
      }
    }
  }
}

void MainWindow::back()
{
  if (_currentView == _ui->systemActions || _currentView == _ui->gameActions)
  {
    clearSelection(_currentView);
    _currentView = _ui->items;
  }
  else
  {
    if (_system != hs::MAIN_MENU)
    {
      switchToSystem(hs::MAIN_MENU);
    }
    else
    {
      exit();
    }
  }
}

void MainWindow::run()
{
  QString romName = _ui->items->currentIndex().data(hs::NameRole).toString();

  QProcess* process = new QProcess(this);
  connect(process,SIGNAL(started()),this,SLOT(hyperLaunchStarted()));
  connect(process,SIGNAL(finished(int)),this,SLOT(hyperLaunchFinished()));

  QSettings iniSettings(QDir(hs::ROOT_FOLDER).absoluteFilePath(QString("config/%1.ini").arg(_system)),QSettings::IniFormat,this);

  if (!iniSettings.value("roms/pcgame").toBool())
  {
    process->start(QDir(hs::ROOT_FOLDER).absoluteFilePath("runner.exe"),QStringList() << _system << romName);
  }
  else
  {
    QString realExe = QFileInfo(QDir(hs::ROOT_FOLDER).absoluteFilePath(QString("romfiles/%1/%2.lnk").arg(_system).arg(romName))).symLinkTarget();
    process->start(realExe,QStringList());
  }
}

void MainWindow::exit()
{
  _exiting = true;

  _ui->leftFrame->hide();
  _ui->rightFrame->hide();
  _ui->videoPlayer->pause();

  ExitDialog dlg(this);
  QPoint p(width()/2-dlg.width()/2,height()/2-dlg.height()/2);
  dlg.setInput(_inputThread,_eventBlocker);
  dlg.setBackground(QPixmap(":/backgrounds/systems.jpg").copy(p.x(),p.y(),dlg.width(),dlg.height()));
  dlg.move(p);
  dlg.exec();

  _exiting = false;

  if (dlg.result() == QDialog::Rejected || dlg.action() == ExitDialog::Cancel)
  {
    _ui->leftFrame->show();
    _ui->rightFrame->show();
    _ui->videoPlayer->play();
  }

  else if (dlg.action() == ExitDialog::ReturnToDesktop)
  {
    QApplication::exit();
  }

  else if (dlg.action() == ExitDialog::Shutdown)
  {
    QProcess::startDetached("shutdown",QStringList() << "-s" << "-t" << "0");
    QApplication::exit();
  }
}

void MainWindow::hyperLaunchStarted()
{
  _oldSize = size();
  _oldPos = pos();

  _ui->videoPlayer->pause();
  _inputThread->setPaused(true);
  hide();
}

void MainWindow::hyperLaunchFinished()
{
  sender()->deleteLater();
  _ui->videoPlayer->play();
  _inputThread->setPaused(false);

  move(_oldPos);
  resize(_oldSize);
  showFullScreen();
}

void MainWindow::moveRow(QListView *view, int movement)
{
  if (view->model()->rowCount() == 0)
    return;

  int row = view->currentIndex().row();
  int originalRow = row;
  bool ok = true;
  do
  {
    row += movement;

    if (row >= view->model()->rowCount())
      row = 0;
    else if (row<0)
      row=view->model()->rowCount()-1;

    if (row == originalRow)
      return;

    Qt::ItemFlags f = view->model()->index(row,0).flags();
    ok = f.testFlag(Qt::ItemIsEnabled) &&
         f.testFlag(Qt::ItemIsSelectable) &&
         !view->isRowHidden(row);
  }
  while (!ok);

  view->setCurrentIndex(view->model()->index(row,0));
}

void MainWindow::pageRow(QListView *view, int movement)
{
  if (view->model()->rowCount() == 0)
    return;

  int row = view->currentIndex().row();
  QChar seekChar = view->currentIndex().data().toString().at(0);
  int originalRow = row;
  bool ok = true;
  do
  {
    row += movement;

    if (row >= view->model()->rowCount())
      row = 0;
    else if (row<0)
      row=view->model()->rowCount()-1;

    if (row == originalRow)
      return;

    Qt::ItemFlags f = view->model()->index(row,0).flags();
    ok = f.testFlag(Qt::ItemIsEnabled) &&
         f.testFlag(Qt::ItemIsSelectable) &&
         !view->isRowHidden(row) &&
         view->model()->index(row,0).data().toString().at(0) != seekChar;
  }
  while (!ok);

  view->setCurrentIndex(view->model()->index(row,0));
}

void MainWindow::clearSelection(QListView *view)
{
  view->setCurrentIndex(QModelIndex());
}

int MainWindow::selectedRow(QListView *view)
{
  return view->selectionModel()->currentIndex().row();
}

void MainWindow::playSound(const QString &url)
{
  PlaySound(url.utf16(), 0, SND_ASYNC);
}

